package de.justphil.websocketclient;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.regex.Pattern;


public class WebSocketHandshake {	
	private String key = null;
	private byte[] expectedServerResponse = null;
	
	private URI url = null;
	//private String origin = null;
	private String protocol = null;
	
	private static final String CRLF = "\r\n";
	private static final String GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
	
	
	public WebSocketHandshake(URI url, String protocol) {
		this.url = url;
		this.protocol = protocol;
		generateKeys();
	}
	
	
	public String getProtocol() {
		return protocol;
	}


	public byte[] getHandshake() {
		String path = url.getPath();
		String host = url.getHost();
		//origin = "http://" + host;
		
		StringBuilder sb = new StringBuilder();
		
		sb.append("GET ").append(path).append(" HTTP/1.1")			.append(CRLF);
		sb.append("Host: ").append(host)							.append(CRLF);
		sb.append("Upgrade: websocket")								.append(CRLF);
		sb.append("Connection: Upgrade")							.append(CRLF);
		sb.append("Sec-WebSocket-Key: ").append(key)				.append(CRLF);
		
		if (protocol != null ) {
			sb.append("Sec-WebSocket-Protocol: ").append(protocol).append(CRLF);
		}
		
		sb.append("Sec-WebSocket-Version: 13")						.append(CRLF);
		sb.append(CRLF);
				
		try {
			return sb.toString().getBytes("ISO-8859-1");
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException("Charset 'ISO-8859-1' is not supported on this platform!");
		}
	}
	
	
	public void verifyServerResponse(String secWebSocketAccept) throws WebSocketException {
		String[] segs = secWebSocketAccept.split( Pattern.quote( ": " ) );
		
		if (segs.length != 2) {
			throw new WebSocketException("Not a WebSocket Server");
		}
		
		String serverKey = segs[1].trim();
		
		if (!serverKey.equals(new String(expectedServerResponse))) {
			throw new WebSocketException("Not a WebSocket Server");
		}
	}
	
	
	public void verifyServerStatusLine(String statusLine) throws WebSocketException {
		int statusCode = Integer.valueOf(statusLine.substring(9, 12));
		
		if (statusCode == 407) {
			throw new WebSocketException("connection failed: proxy authentication not supported");
		}
		else if (statusCode == 404) {
			throw new WebSocketException("connection failed: 404 not found");
		}
		else if (statusCode != 101) {
			throw new WebSocketException("connection failed: unknown status code " + statusCode);
		}
	}
	
	
	public void verifyServerHandshakeHeaders(HashMap<String, String> headers) throws WebSocketException {
		if (!headers.get("Upgrade").equals("websocket")) {
			throw new WebSocketException("connection failed: missing header field in server handshake: Upgrade");
		}
		else if (!headers.get("Connection").equals("Upgrade")) {
			throw new WebSocketException("connection failed: missing header field in server handshake: Connection");
		}
		
		/*
		else if (!headers.get("Sec-WebSocket-Origin").equals(origin)) {
			throw new WebSocketException("connection failed: missing header field in server handshake: Sec-WebSocket-Origin");
		}
		*/
	}
		
	
	private void generateKeys() {
//		Log.i("WebSocketHandshake", "generateKeys()");
		
		long time = System.currentTimeMillis();
		
		try {
			key = Base64.encodeToString(String.valueOf(time).getBytes("UTF-8"), false);
		} catch (UnsupportedEncodingException e1) {
			throw new RuntimeException("Charset 'UTF-8' is not supported on this platform!");
		}
		
//		Log.i("WebSocketHandshake", "key => " + key);
		
		String exp = key + GUID;
		
		byte[] sha1Hash = null;
		
		try {
			sha1Hash = SHA1.encode(exp);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {

		}

		expectedServerResponse = Base64.encodeToByte(sha1Hash, false);
		
//		Log.i("WebSocketHandshake", "exp => " + new String(expectedServerResponse));
	}
}
